package com.zhouyu.mysql;

import com.zhouyu.mysql.core.*;
import com.zhouyu.mysql.dict.DictColumn;
import com.zhouyu.mysql.dict.DictTable;
import com.zhouyu.mysql.dict.SystemDict;
import com.zhouyu.mysql.page.FspHdrPage;
import com.zhouyu.mysql.page.IndexPage;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.FromItemVisitorAdapter;
import net.sf.jsqlparser.statement.select.PlainSelect;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static com.zhouyu.mysql.page.IndexPage.PAGE_NEW_INFIMUM;
import static com.zhouyu.mysql.page.IndexPage.PAGE_NEW_SUPREMUM;

/**
 * 大都督周瑜（我的微信: dadudu6789）
 */
public class HaInnodb {

    public void createTable(CreateTable createTableStatement) {

        String tableName = createTableStatement.getTable().getName();

        if (SystemDict.getInstance().getNameTables().containsKey(tableName)) {
            throw new RuntimeException("表已经存在");
        }

        // 生成一个新id
        int tableId = SystemDict.getInstance().addMaxTableId();
        int spaceId = SystemDict.getInstance().addMaxSpaceId();

        // 创建t1.ibd文件，返回文件路径
        Path tableSpacePath = SpaceUtil.createUserTableSpace(tableName);

        DictTable dictTable = new DictTable();
        dictTable.setTableId(tableId);
        dictTable.setSpaceId(spaceId);
        dictTable.setTableName(tableName);
        dictTable.setPageNo(3);
        dictTable.setPath(tableSpacePath.toString());

        List<DictColumn> dictColumnList = new ArrayList<>();
        List<ColumnDefinition> columnDefinitions = createTableStatement.getColumnDefinitions();
        for (int i = 0; i < columnDefinitions.size(); i++) {
            ColumnDefinition columnDefinition = columnDefinitions.get(i);

            DictColumn dictColumn = new DictColumn();
            dictColumn.setTableId(tableId);
            dictColumn.setName(columnDefinition.getColumnName());
            String dataType = columnDefinition.getColDataType().getDataType();
            dictColumn.setType(dataType);
            dictColumn.setColNo(i);
            if ("int".equals(dataType)) {
                dictColumn.setLen(4);
            } else {
                // 实际应该解析出varchar(11)中的11，然后还要知道编码集，才能算出字段长度
                // 这里就直接写死了，方便模拟
                dictColumn.setLen(2);
            }
            dictColumnList.add(dictColumn);
        }

        dictTable.setDictColumnList(dictColumnList);

        // 将当前创建的表元数据添加到数据字典
        SystemDict.getInstance().getNameTables().put(tableName, dictTable);
        SystemDict.getInstance().getIdTables().put(tableId, dictTable);
        SystemDict.getInstance().getSpaceIdTables().put(spaceId, dictTable);

        // 把SystemDict对象持久化
        SystemDict.getInstance().serialize();

        // 初始化FspHdrPage
        FspHdrPage fspHdrPage = SpaceUtil.getFspHdrPage(spaceId);
        fspHdrPage.init_file_header(spaceId, 0);
        fspHdrPage.fil_page_set_type(8);  // 源码中8表示FSP_HDR
        fspHdrPage.set_fsp_size(4);       // 当前表空间中只有1页，pageNo为0
        PageUtil.flushPage(fspHdrPage);   // 记得把页的修改持久化到ibd文件中去

        // 初始化根页
        IndexPage rootPage = PageUtil.readPage(spaceId, dictTable.getPageNo());
        rootPage.init_file_header(spaceId, dictTable.getPageNo());
        rootPage.init_page_header();
        PageUtil.flushPage(rootPage);

    }

    public void insert(Insert insertStatement) {

        // 插入到哪个表空间的哪一页
        String tableName = insertStatement.getTable().getName();
        DictTable dictTable = SystemDict.getInstance().getNameTables().get(tableName);

        // 将insert转成DTuple，里面包含了插入的字段和数据
        DTuple dTuple = DTupleUtil.convert(insertStatement);

        // 先拿到表聚集索引对应的根页
        IndexPage rootPage = PageUtil.readPage(dictTable.getSpaceId(), dictTable.getPageNo());

        if (rootPage.page_is_leaf()) {
            // 判断根页是否满了
            if (rootPage.hasFreeSpace()) {
                // 有空闲空间，则直接插入记录
                PageUtil.insert_row(rootPage, dTuple);
            } else {
                // 没有空闲空间，则先新开辟一页
                int newPageNo = PageUtil.createPage(dictTable.getSpaceId());
                IndexPage newPage = PageUtil.readPage(dictTable.getSpaceId(), newPageNo);

                // 把新记录插入到新页
                PageUtil.insert_row(newPage, dTuple);

                // 因为是根页满了，所以需要再新建一页，用来复制现在根页中的内容
                int copyPageNo = PageUtil.createPage(dictTable.getSpaceId());
                IndexPage copyPage = PageUtil.readPage(dictTable.getSpaceId(), copyPageNo);

                rootPage.getPageByteBuffer().clear();
                copyPage.getPageByteBuffer().clear();
                copyPage.getPageByteBuffer().put(rootPage.getPageByteBuffer());
                copyPage.fil_page_set_page_offset(copyPageNo);      // 只需要根页的内容，不需要根页的页号，页号还是用新页自己的

                copyPage.fil_page_set_next(newPageNo);
                newPage.fil_page_set_prev(copyPageNo);

                PageUtil.flushPage(newPage);
                PageUtil.flushPage(copyPage);

                // 将根页用来存索引记录
                // 重新初始化相关属性，相当于根页中没有存内容
                rootPage.init_page_header();
                rootPage.btr_page_set_level(1);  // 根节点的level为1
                // 构造存入根页的索引记录，然后插入到根页即可，copyPage对应一条记录，newPage对应一条记录
                DTuple copyPageTuple = DTupleUtil.generateIndexRecord(copyPage);
                DTuple newPageTuple = DTupleUtil.generateIndexRecord(newPage);

                PageUtil.insert_row(rootPage, copyPageTuple);
                PageUtil.insert_row(rootPage, newPageTuple);
            }
        } else {
            // 根节点不是叶子节点，则应该根据根节点的索引记录和插入记录比较出插入记录应该插在哪一页，相当于做一次索引查询

            // 会返回根节点中第一个id小于dTuple中的id的索引记录
            int index_rec = PageUtil.page_cur_search_with_match_bytes(rootPage, dTuple, false);
            int pageNo = RecUtil.getRecPageNo(rootPage, index_rec);

            IndexPage indexPage = PageUtil.readPage(rootPage.fil_page_get_space_id(), pageNo);

            // 如果对应页有空闲空间，那就将记录插入进去
            // 这里我们没有考虑页要拆分的问题，我们只支持按id自增的方式插入数据
            if (indexPage.hasFreeSpace()) {
                PageUtil.insert_row(indexPage, dTuple);
            } else {
                // 如果没有空闲空间，则需要生成新的页，把新记录插入新页，并且生成索引记录插入到根页
                int newPageNo = PageUtil.createPage(rootPage.fil_page_get_space_id());
                IndexPage newPage = PageUtil.readPage(dictTable.getSpaceId(), newPageNo);

                indexPage.fil_page_set_next(newPageNo);
                newPage.fil_page_set_prev(pageNo);

                PageUtil.flushPage(newPage);
                PageUtil.flushPage(indexPage);

                // 把新记录插入到新页
                PageUtil.insert_row(newPage, dTuple);
                DTuple newPageTuple = DTupleUtil.generateIndexRecord(newPage);
                PageUtil.insert_row(rootPage, newPageTuple);
            }
        }


    }

    public SelectOneResult select_one(DictTable dictTable, PageCur pageCur, DTuple dTuple) {

        IndexPage indexPage = PageUtil.readPage(dictTable.getSpaceId(), pageCur.getPageNo());

        // 当前记录是最大记录则直接返回
        if (indexPage.is_last() && pageCur.getRec() == PAGE_NEW_SUPREMUM) {
            return SelectOneResult.builder().endOfIndex(true).build(); //表示所有记录都遍历完了
        }

        int next_rec;
        HashMap<String, Object> result;
        PageCur nextPageCur;

        // 找出和dTuple匹配的记录
        if (!dTuple.getFields().isEmpty()) {
            // 根据dTuple进行匹配，找到匹配的记录
            next_rec = PageUtil.page_cur_search_with_match_bytes(indexPage, dTuple, true);

            // 表示在当前页中没找到，那检查是否还有下一页，如果有则定位到下一页的最小记录，然后返回
            if (next_rec == PAGE_NEW_SUPREMUM) {
                if (indexPage.is_last()) {
                    return SelectOneResult.builder().endOfIndex(true).build(); //表示所有记录都遍历完了
                } else {
                    result = null;
                    nextPageCur = new PageCur();
                    nextPageCur.setPageNo(indexPage.fil_page_get_next());
                    nextPageCur.setRec(PAGE_NEW_INFIMUM);
                }
            } else {
                // 找到了，把下一条记录直接设置为最后一页的最大记录，这样就能终止遍历
                result = RecUtil.getRecContent(indexPage, next_rec);
                nextPageCur = new PageCur();
                nextPageCur.setPageNo(indexPage.fil_page_get_page_offset());
                nextPageCur.setRec(PAGE_NEW_SUPREMUM);
            }
        } else {
            // 全表扫描，直接取一下条
            next_rec = RecUtil.rec_get_next_offs(indexPage, pageCur.getRec());

            // 如果下一条记录是当前页的最大记录，则判断是不是有下一页，如果有则将nextPageCur定位到下一页的最小记录
            if (next_rec == PAGE_NEW_SUPREMUM) {
                if (indexPage.is_last()) {
                    return SelectOneResult.builder().endOfIndex(true).build(); //表示所有记录都遍历完了
                } else {
                    result = null;
                    nextPageCur = new PageCur();
                    nextPageCur.setPageNo(indexPage.fil_page_get_next());
                    nextPageCur.setRec(PAGE_NEW_INFIMUM);
                }
            } else {
                result = RecUtil.getRecContent(indexPage, next_rec);
                nextPageCur = new PageCur();
                nextPageCur.setPageNo(indexPage.fil_page_get_page_offset());
                nextPageCur.setRec(next_rec);
            }
        }

        return SelectOneResult.builder()
                .endOfIndex(false)
                .recContent(result)
                .pageCur(nextPageCur)
                .build();


    }

    public void alter(Alter alterStatement) {

    }
}
